package com.swak.frame.catchlog;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.springframework.core.Ordered;

import com.alibaba.fastjson.JSON;
import com.swak.frame.dto.base.SysRestCode;
import com.swak.frame.exception.ApiException;
import com.swak.frame.exception.SysException;
import com.swak.frame.handler.ResponseHandler;
import com.swak.frame.logger.AspectLoggerFactory;

/**
 * Catching and Logging ClassName: CatchLogAspect.java
 * 
 * @author colley.ma
 * @date 2021年3月19日 下午1:41:07
 */
@Aspect
public class CatchLogAspect implements Ordered {

	private int order;

	/**
	 * The syntax of pointcut :
	 * https://blog.csdn.net/zhengchao1991/article/details/53391244
	 */
	@Pointcut("@within(CatchAndLog) && execution(public * *(..))")
	public void pointcut() {
	}

	@Around(value = "pointcut()")
	public Object around(ProceedingJoinPoint joinPoint) {
		long startTime = System.currentTimeMillis();
		Logger logger = AspectLoggerFactory.getLogger(joinPoint.getTarget().getClass());
		logRequest(joinPoint, logger);
		Object response = null;
		try {
			response = joinPoint.proceed();
		} catch (Throwable e) {
			response = handleException(joinPoint, e, logger);
		} finally {
			logResponse(startTime, response, logger);
		}
		return response;
	}

	private Object handleException(ProceedingJoinPoint joinPoint, Throwable e, Logger logger) {
		MethodSignature ms = (MethodSignature) joinPoint.getSignature();
		Class<?> returnType = ms.getReturnType();
		if (e instanceof ApiException) {
			logger.warn("BIZ EXCEPTION : " + e.getMessage());
			// 在Debug的时候，对于ApiException也打印堆栈
			ApiException ex = (ApiException) e;
			logger.warn("[biz-error]#method={},retCode:{},retMessage:{},trace:{}",
					new Object[] { ms.getMethod().getName(), ex.getErrCode(), ex.getErrMessage(), e.getMessage() });
			return ResponseHandler.handle(returnType, (ApiException) e);
		}
		if (e instanceof SysException) {
			logger.error("SYS EXCEPTION :");
			logger.error("[biz-error]#" + ms.getMethod().getName() + "接口异常！", e);
			return ResponseHandler.handle(returnType, (SysException) e);
		}

		logger.error("UNKNOWN EXCEPTION :");
		logger.error("[biz-error]#" + ms.getMethod().getName() + "接口异常！", e);
		return ResponseHandler.handle(returnType, SysRestCode.SYS_ERR.getRetCode(),
				SysRestCode.SYS_ERR.getRetMessage());
	}

	private void logResponse(long startTime, Object response, Logger logger) {
		try {
			long endTime = System.currentTimeMillis();
			logger.debug("RESPONSE : " + JSON.toJSONString(response));
			logger.debug("COST : " + (endTime - startTime) + "ms");
		} catch (Exception e) {
			// swallow it
			logger.error("logResponse error : " + e);
		}
	}

	private void logRequest(ProceedingJoinPoint joinPoint, Logger logger) {
		try {
			logger.debug("START PROCESSING: " + joinPoint.getSignature().toShortString());
			Object[] args = joinPoint.getArgs();
			for (Object arg : args) {
				logger.debug("REQUEST : " + JSON.toJSONString(arg));
			}
		} catch (Exception e) {
			// swallow it
			logger.error("logReqeust error : " + e);
		}
	}

	@Override
	public int getOrder() {
		return order;
	}

	public void setOrder(int order) {
		this.order = order;
	}

}
